package org.dru.clay.util.file;

import org.dru.clay.util.functional.CollectionUtils;
import org.dru.clay.util.functional.Function;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

/**
 * FileFunctions
 * User: joakimd
 * Date: 2013-07-24
 * Time: 13:32
 */
public final class FileFunctions {
    private FileFunctions() {
    }

    public static Function<File, File> parentFile() {
        return FileFileFunction.PARENT;
    }

    public static Function<File, String> path() {
        return FileStringFunction.PATH;
    }

    public static Function<File, String> canonicalPath() {
        return FileStringFunction.CANONICAL_PATH;
    }

    public static Function<String, File> file() {
        return StringFileFunction.FILE;
    }

    /**
     * @return function(dir) -> collection of explicit list and directories.
     */
    public static Function<File, Collection<File>> list() {
        return FileCollection.LIST;
    }

    /**
     * @return function(dir) -> collection of explicit list.
     */
    public static Function<File, Collection<File>> fileList() {
        return FileCollection.FILE_LIST;
    }

    /**
     * @return function(dir) -> collection of recursive list.
     */
    public static Function<File, Collection<File>> fileTree() {
        return FileCollection.FILE_TREE;
    }

    private static enum FileFileFunction implements Function<File, File> {
        PARENT {
            @Override
            public File apply(final File value) {
                return value.getParentFile();
            }
        }
    }

    private static enum FileStringFunction implements Function<File, String> {
        PATH {
            @Override
            public String apply(final File value) {
                return value.getPath();
            }

        },
        CANONICAL_PATH {
            @Override
            public String apply(final File value) {
                try {
                    return value.getCanonicalPath();
                } catch (final IOException exc) {
                    throw new RuntimeException(exc);
                }
            }
        }
    }

    private static enum StringFileFunction implements Function<String, File> {
        FILE {
            @Override
            public File apply(final String value) {
                return new File(value);
            }
        }
    }

    private static enum FileCollection implements Function<File, Collection<File>> {
        LIST {
            @Override
            public Collection<File> apply(final File value) {
                final File[] files = value.listFiles();
                return (files != null ? Arrays.asList(files) : Collections.<File>emptyList());
            }
        },
        FILE_LIST {
            @Override
            public Collection<File> apply(final File value) {
                return CollectionUtils.filter(list().apply(value), FilePredicates.isFile());
            }
        },
        FILE_TREE {
            @Override
            public Collection<File> apply(final File value) {
                final Collection<File> result = new ArrayList<File>();
                for (final File file : list().apply(value)) {
                    if (file.isDirectory()) {
                        result.addAll(fileTree().apply(file));
                    } else {
                        result.add(file);
                    }
                }
                return result;
            }
        }
    }
}
